/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkRtmpClient.h"
#include "MkEvent/MkEvent.h"
#include "MkUtil/MkLog.h"
#include "MkUtil/MkUtil.h"
#include "MkCoreConfig.h"
#include "MkAmf/MkAmf.h"
#include "MkRtmpMuxer.h"


#ifndef _WIN32
static int ClkTck = 0;
#include <sys/times.h>
#endif
CMkRtmpClient::CMkRtmpClient(CMkEvent* pEvent, CMkTimer* pTimer, const MkEventRemovedCallback& RemoveCb)
    : CMkTcpClient(pEvent, pTimer, RemoveCb)
    , m_StreamType("live")
    , m_AudioSupports(AudioSupportAll)
    , m_VideoSupports(VideoSupportAll)
    , m_VideoFunction(SupportVideoClientSeek)
    , m_nBWCheckCounter(0)
    , m_SeekTime(0)
    , m_RtmpPlayFlags(0)
{
    Int8 pBuf[RtmpSigSize] = { 0 };
    Uint32 NowTime;
#ifdef _WIN32
    NowTime = timeGetTime();
#else
    struct tms t;
    if (0 == ClkTck) {
        ClkTck = sysconf(_SC_CLK_TCK);
    }
    NowTime = times(&t) * 1000 / ClkTck;
#endif
    pBuf[0] = (NowTime >> 24) & 0xFF;
    pBuf[1] = (NowTime >> 16) & 0xFF;
    pBuf[2] = (NowTime >> 8) & 0xFF;
    pBuf[3] = NowTime & 0xFF;
    for (int i = 8; i < RtmpSigSize; i++) {
        pBuf[i] = rand() & 0xFF;
    }
    m_LocalSig = MkString(pBuf, RtmpSigSize);
}


Uint32 CMkRtmpClient::ConnectUrl(const MkString& Url)
{
    MkString Domain;
    MkString Host;
    Uint16 Port;
    Uint32 ErrorCode = CMkRtmpUtil::ParseRtmpUrl(Url, m_Protocol, Domain, Port, m_AppName, m_Path);
    MkCheckErrorLog(ErrorCode, "parse rtmp url:%s failed\n", Url.c_str());
    m_TcUrl = Url.substr(0, Url.size() - m_Path.size() - 1);
    CMkUtil::GetOneIpByHostName(Domain, Host);
    return ConnectServer(Host, Port, [this]() {
        SendHandSharkeC0C1();
    });
}

Uint32 CMkRtmpClient::OnRecv()
{
    Uint32 RecvLen = m_RecvMaxLen - m_nRecvOffset;
    if (0 == RecvLen) {
        Close();
        MkWarningLog("recv error buffer\n");
        return SocketClosed;
    }
    Uint32 ErrorCode = Recv(m_pRecvBuffer + m_nRecvOffset, RecvLen);
    MkCheckErrorLog(ErrorCode, "rtmp client recv failed\n");
    //MkDebugLog("recv len:%d\n", RecvLen);
    m_nRecvOffset += RecvLen;
    Uint32 nUsed = 0;
    while (nUsed + 12 <= m_nRecvOffset) {
        Uint32 CurUsed = 0;
        switch (m_RtmpState) {
        case RtmpStateC0:
        case RtmpStateC1:
            ErrorCode = ParseHandeSharkeS0S1(m_pRecvBuffer + nUsed, m_nRecvOffset - nUsed, CurUsed);
            if (NoneError == ErrorCode) {
                //MkDebugLog("rtmp handshakes0s1 use:%d\n", CurUsed);
                m_RtmpState = RtmpStateS1;
                SendHandSharkeC2();
                nUsed += CurUsed;
            }
            break;
        case RtmpStateC2:
            ErrorCode = ParseHandeSharkeS2(m_pRecvBuffer + nUsed, m_nRecvOffset - nUsed, CurUsed);
            if (NoneError == ErrorCode) {
                m_RtmpState = RtmpStateS2;
                nUsed += CurUsed;
                OnHandSharked();
            }
            break;
        case RtmpStateS2:
            ErrorCode = ParseRtmpMessage(m_pRecvBuffer + nUsed, m_nRecvOffset - nUsed, CurUsed);
            if (NoneError == ErrorCode) {
                nUsed += CurUsed;
            }
            break;
        default:
            break;
        }
        if (NotHasEnoughBuffer == ErrorCode) {
            ErrorCode = NoneError;
            break;
        } else if (NoneError != ErrorCode) {
            MkErrorLog("rtmp client return error msg\n");
            Close();
            return SocketClosed;
        }
    }
    if (nUsed > m_nRecvOffset) {
        MkErrorLog("rtmp recv error msg\n");
        Close();
        return SocketClosed;
    } else if (NoneError == ErrorCode && nUsed < m_nRecvOffset) {
        memmove(m_pRecvBuffer, m_pRecvBuffer + nUsed, m_nRecvOffset);
    }
    m_nRecvOffset -= nUsed;
    return NoneError;
}

Uint32 CMkRtmpClient::OnClose()
{
    CMkTcpClient::OnClose();
    MkErrorLog("rtmp  server closed\n");
    return NoneError;
}

Uint32 CMkRtmpClient::SendHandSharkeC0C1()
{
    Uint8 Version = RtmpVersion;
    CMkBufferList SendBuf;
    SendBuf.Append((&Version), 1);
    SendBuf.Append((void*)m_LocalSig.c_str(), m_LocalSig.size());
    m_RtmpState = RtmpStateC1;
    CMkSocket::Send(SendBuf);
    return NoneError;
}

Uint32 CMkRtmpClient::SendHandSharkeC2()
{
    m_RtmpState = RtmpStateC2;
    CMkSocket::Send((Uint8*)m_RemoteSig.c_str(), m_RemoteSig.size());
    return NoneError;
}

Uint32 CMkRtmpClient::SendPong()
{
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketPongResponse(m_TimeStamp, m_OutChunkSize, m_pPacketBuffer, m_PacketMaxLen, m_nPacketOffset, BufferList);
    MkCheckErrorLog(ErrorCode, "packet pong failed\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendConnect(BOOL bSender)
{
    /*
    +----------------+---------+---------------------------------------+
    | Field Name     | Type    | Description                           |
    +--------------- +---------+---------------------------------------+
    | Command Name   | String  | Name of the command. Set to “connect”.|
    +----------------+---------+---------------------------------------+
    | Transaction ID | Number  | Always set to 1.                      |
    +----------------+---------+---------------------------------------+
    | Command Object | Object  | Command information object which has  |
    |                |         | the name-value pairs.                 |
    +----------------+---------+---------------------------------------+
    | Optional User  | Object  | Any optional information              |
    | Arguements     |         |                                       |
    +----------------+---------+---------------------------------------+
    */

    MkAmfObject CmdObject(MkAmfTypeObject);
    CmdObject.VecObjectValue.push_back(MkAmfObject("app", MkAmfString, m_AppName));
    //publisher
    if (bSender) {
        CmdObject.VecObjectValue.push_back(MkAmfObject("type", MkAmfString, "nonprivate"));
    }
    CmdObject.VecObjectValue.push_back(MkAmfObject("flashVer", MkAmfString, "MkRtmpVersion " + MkString(VERSION)));
    //CmdObject.VecObjectValue.push_back(MkAmfObject("flashVer", MkAmfString, "LNX 9,0,124,2"));
    CmdObject.VecObjectValue.push_back(MkAmfObject("tcUrl", MkAmfString, m_TcUrl));
    if (!bSender) {
        CmdObject.VecObjectValue.push_back(MkAmfObject("fpad", MkAmfBoolean, (BOOL)FALSE));
        CmdObject.VecObjectValue.push_back(MkAmfObject("capabilities", MkAmfNumber, 15.0));
        CmdObject.VecObjectValue.push_back(MkAmfObject("audioCodecs", MkAmfNumber, (float)m_AudioSupports));
        CmdObject.VecObjectValue.push_back(MkAmfObject("videoCodecs", MkAmfNumber, (float)m_VideoSupports));
        CmdObject.VecObjectValue.push_back(MkAmfObject("videoFunction", MkAmfNumber, (float)m_VideoFunction));
        //CmdObject.VecObjectValue.push_back(MkAmfObject("audioCodecs", MkAmfNumber, 4071.0));
        //CmdObject.VecObjectValue.push_back(MkAmfObject("videoCodecs", MkAmfNumber, 252.0));
        //CmdObject.VecObjectValue.push_back(MkAmfObject("videoFunction", MkAmfNumber, 1.0));
    }
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(CmdObject);
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "connect", 1, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendReleaseStream()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfString, m_Path));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "releaseStream", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "releaseStream");
    //MkDebugLog("send realese stream\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendFcPublish()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfString, m_Path));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "FCPublish", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "FCPublish");
    //MkDebugLog("send pc publish\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendChunkSize(Uint32 ChunkSize)
{
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketSetChunkSize(ChunkSize, m_OutChunkSize, m_pPacketBuffer, m_PacketMaxLen, m_nPacketOffset, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    m_OutChunkSize = ChunkSize;
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendWindownAcknowledgementSize()
{
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketWindownAcknowledgementSize(m_nServerBw, m_OutChunkSize, m_pPacketBuffer, m_PacketMaxLen, m_nPacketOffset, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendCreateStream()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "createStream", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "createStream");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendGetStreamLength()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfString, m_Path));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "getStreamLength", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "getStreamLength");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendUsherToken(const MkString& Token)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfString, Token));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "NetStream.Authenticate.UsherToken", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "NetStream.Authenticate.UsherToken");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendFCSubscribe(const MkString& SubscribePath)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfString, SubscribePath));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "FCSubscribe", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "FCSubscribe");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendPublish()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfString, m_Path));
    VecObj.push_back(MkAmfObject("", MkAmfString, m_StreamType));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "publish", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "publish");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendPlay(const MkString& StreamFormat/*=""*/, int StrartSeconds/*=0*/, int Duration/*=0*/, BOOL bReset/*=FALSE*/)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    if (StreamFormat.empty()) {
        VecObj.push_back(MkAmfObject("", MkAmfString, m_Path));
    } else {
        VecObj.push_back(MkAmfObject("", MkAmfString, StreamFormat + ":" + m_Path));
    }

    /* An optional parameter that specifies the start time in seconds. The default value is -2, which means the subscriber first tries to play the live stream
    specified in the Stream Name field. If a live stream of that name is not found,it plays the recorded stream specified in the Stream Name field. If you pass -1
    in the Start field, only the live stream specified in the Stream Name field is played. If you pass 0 or a positive number in the Start field, a recorded stream
    specified in the Stream Name field is played beginning from the time specified in the Start field. If no recorded stream is found, the next item in the playlist is played.
    */
    if (0 != StrartSeconds) {
        VecObj.push_back(MkAmfObject("", MkAmfNumber, (double)StrartSeconds));
    }

    /*The -1 value means  a live stream is played until it is no longer available or a recorded stream is  played until it ends. If u pass 0, it plays the single frame since the time
    specified in the Start field from the beginning of a recorded stream. It is assumed that the value specified in the Start field is equal to or greater than 0*/
    if (0 != Duration) {
        VecObj.push_back(MkAmfObject("", MkAmfNumber, (double)Duration));
    }
    /* An optional Boolean value or number that specifies whether to flush any previous playlist.*/
    if (bReset) {
        VecObj.push_back(MkAmfObject("", MkAmfBoolean, bReset));
    }
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "play", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "play");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendPlay2(Uint32 StartTime, const MkString& OldStreamName, Uint32 Duration, const MkString& Transition)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    MkAmfObject StartTimeObj;
    StartTimeObj.DataType = MkAmfTypeObject;
    StartTimeObj.VecObjectValue.push_back(MkAmfObject("", MkAmfNumber, (double)StartTime));
    VecObj.push_back(StartTimeObj);

    MkAmfObject OldStreamObj;
    OldStreamObj.DataType = MkAmfTypeObject;
    OldStreamObj.VecObjectValue.push_back(MkAmfObject("", MkAmfString, OldStreamName));
    VecObj.push_back(OldStreamObj);

    MkAmfObject DurationObj;
    DurationObj.DataType = MkAmfTypeObject;
    DurationObj.VecObjectValue.push_back(MkAmfObject("", MkAmfNumber, (double)Duration));
    VecObj.push_back(DurationObj);

    MkAmfObject TransitionObj;
    TransitionObj.DataType = MkAmfTypeObject;
    TransitionObj.VecObjectValue.push_back(MkAmfObject("", MkAmfString, Transition));
    VecObj.push_back(TransitionObj);
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "play", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "play");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendAcknowledgement()
{
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketAcknowledgement(m_SendRecvSize, m_OutChunkSize, m_pPacketBuffer, m_PacketMaxLen, m_nPacketOffset, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendDeleteStream()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfNumber, (double)RtmpDefaultStreamId));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "deleteStream", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "deleteStream");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendSeek(Uint32 MilliSeconds)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfNumber, (double)MilliSeconds));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "seek", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "seek");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendReceiveVideo(BOOL bSend)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfBoolean, bSend));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "receiveVideo", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "receiveVideo");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendReceiveAudio(BOOL bSend)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfBoolean, bSend));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "receiveAudio", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "receiveAudio");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendPause(BOOL bPause, Uint32 MilliSeconds)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfBoolean, bPause));
    VecObj.push_back(MkAmfObject("", MkAmfNumber, (double)MilliSeconds));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "pause", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "pause");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendPlaylist()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));

    MkAmfObject ArrayObj;
    ArrayObj.DataType = MkAmfEcmaArray;


    VecObj.push_back(ArrayObj);
    VecObj.push_back(MkAmfObject("", MkAmfString, m_Path));
    VecObj.push_back(MkAmfObject(MkAmfObjectEnd));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketCommandMessage(m_OutChunkSize, "set_playlist", m_TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    AddMapTransaction(m_TransactionId, "set_playlist");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendCheckBWResult(Uint32 TransactionId)
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    VecObj.push_back(MkAmfObject("", MkAmfNumber, (double)m_nBWCheckCounter++));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketResponseMessage(m_OutChunkSize, "_result", TransactionId, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::SendCheckBW()
{
    MkVector<MkAmfObject> VecObj;
    VecObj.push_back(MkAmfObject(MkAmfNull));
    CMkBufferList BufferList;
    Uint32 ErrorCode = CMkRtmpMuxer::PacketResponseMessage(m_OutChunkSize, "_checkbw", m_TransactionId++, VecObj, m_pPacketBuffer, m_PacketMaxLen, BufferList);
    MkCheckErrorLog(ErrorCode, "packet connect failed\n");
    return Send(BufferList);
}

Uint32 CMkRtmpClient::ParseHandeSharkeS0S1(const Uint8* pBuffer, Uint32 nLen, Uint32& nUsed)
{
    if (RtmpVersion != pBuffer[0]) {
        return InvalidParams;
    }
    if (nLen < RtmpSigSize + 1) {
        return NotHasEnoughBuffer;
    }
    m_RemoteSig = MkString((Int8*)pBuffer + 1, RtmpSigSize);
    nUsed = 1 + RtmpSigSize;
    return NoneError;
}

Uint32 CMkRtmpClient::ParseHandeSharkeS2(const Uint8* pBuffer, Uint32 nLen, Uint32& nUsed)
{
    if (nLen < RtmpSigSize) {
        return NotHasEnoughBuffer;
    }
    if (memcmp(pBuffer, m_LocalSig.c_str(), m_LocalSig.size())) {
        return InvalidParams;
    }
    nUsed = RtmpSigSize;
    return NoneError;
}

Uint32 CMkRtmpClient::OnCommandRequest(const MkString& Command, const MkVector<MkAmfObject>& VecObj)
{
    Uint32 ErrorCode = NoneError;
    if ("onBWDone" == Command) {
        if (0 == m_nBWCheckCounter) {
            ErrorCode = SendCheckBW();
        }
    } else if ("ping" == Command) {
        ErrorCode = SendPong();
    } else if ("_checkbw" == Command) {

    } else if ("close" == Command) {
        Close();
        return SocketClosed;
    } else if ("_onbwcheck" == Command) {
        MkVector<MkAmfObject>::const_iterator it = VecObj.begin();
        if (MkAmfNumber != it->DataType) {
            return InvalidParams;
        }
        ErrorCode = SendCheckBWResult(static_cast<Uint32>(it->NumberValue));
    } else if ("onMetaData" == Command) {
        ErrorCode = OnMetaData(VecObj);
    } else if ("|RtmpSampleAccess" == Command) {
        printf("223");
    }
    return ErrorCode;
}

CMkRtmpClient::~CMkRtmpClient()
{

}